<?php
namespace Com\Pay;

class YipiaolianPay{

    private $config = array();
    private $post_data = array();

    public function __construct($send_data)
    {
        $this->config = $send_data;
    }


    /*生成支付连接*/
    public function getUrl($order_info)
    {
        /* 获取提交的订单交易金额 */
        $price = $order_info['paymoney'];
        $notifyUrl = 'http://' . $_SERVER['HTTP_HOST'] . '/Api/NotifyCallback/YipiaolianNotify';
        $order_sn = $order_info['ordernumber'];
        $this->post_data = array(
            'version' => '4.0',
            'sign_type' => 'SHA256withRSA',
            "partner" => $this->config['mch_id'],
            "out_trade_no" => $order_sn,
            "total_fee" => $price,
            "currency_type" => "RMB",
            "notify_url" => $notifyUrl,
            'return_url' => $order_info['return_url'],
            'order_create_ip' => get_client_ip(),
            'pay_id' => $order_info['pay_id'],
        );
        ksort($this->post_data);
        $this->MakeSign();
        $tar_url = 'https://www.epaylinks.cn/paycenter/v2.0/getoi.do?';
        $urls = $tar_url . http_build_query($this->post_data);
        $result = xmlToArray(file_get_contents($urls));
        if ($result['resp_code'] == '00') {
            $ret = $result['code_url'];
        } else {
            // var_dump($res1);die;
            $ret = '';
        }
        return $ret;
    }


    //回调方法，把返回数据处理成统一规定返回
    public function response(){
        $str = file_get_contents("php://input");
//        file_put_contents('./ypl.txt' , var_export($str , true));
        $params=array();
        parse_str($str,$params);
//        $pu_key = openssl_get_publickey(file_get_contents(getcwd() . '/ThinkPHP/Library/Vendor/yipiaolian/epaylinks_pfx.cer')); // 测试密钥
        $pu_key = openssl_get_publickey(file_get_contents(getcwd() . '/ThinkPHP/Library/Vendor/yipiaolian/yipiaolian_public.cer'));
        $sign = $params["sign"];
        ksort($params);
        $string = mb_convert_encoding($this->ToUrlParams($params),"GBK","UTF-8");
        $result = (bool)openssl_verify($string, base64_decode($sign), $pu_key,"SHA256");
        if($result) {
            if($params['pay_result'] == '1') return true;
        }else{
            return false;
        }
    }

    private static function ToUrlParams($post_data){
        $buff = "";
        foreach ($post_data as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = rtrim($buff, "&");
        return $buff;
    }

    public function MakeSign(){
        //签名步骤一：按字典序排序参数
        $certs = array();
        openssl_pkcs12_read(file_get_contents(getcwd() . '/ThinkPHP/Library/Vendor/yipiaolian/yipiaolian_private.pfx'), $certs, 'yibao@307');
//        openssl_pkcs12_read(file_get_contents(getcwd() . '/ThinkPHP/Library/Vendor/yipiaolian/yzt.pfx'), $certs, 'yzt20160728'); //其中password为你的证书密码
        $this->post_data['certId'] = CertSerialUtil::getSerial($certs['cert'], $errMsg);
        ksort($this->post_data);
        $string = $this->ToUrlParams($this->post_data);
        openssl_sign($string, $signature, $certs['pkey'], "SHA256");
        $this->post_data['sign'] = base64_encode($signature);
    }
}




class CertSerialUtil {

    private static function bytesToInteger($bytes) {
        $val = 0;
        for($i = 0; $i < count ( $bytes ); $i ++) {
            // 			$val += (($bytes [$i] & 0xff) << (8 * (count ( $bytes ) - 1 - $i)));
            $val += $bytes [$i] * pow(256, count ( $bytes ) - 1 - $i);
            // 			echo $val . "<br>\n";
        }
        return $val;
    }

    private static function bytesToBigInteger($bytes) {
        $val = 0;
        for($i = 0; $i < count ( $bytes ); $i ++) {
            $val = bcadd($val, bcmul($bytes [$i], bcpow(256, count ( $bytes ) - 1 - $i)));
            // 			echo $val . "<br>\n";
        }
        return $val;
    }

    private static function toStr($bytes) {
        $str = '';
        foreach($bytes as $ch) {
            $str .= chr($ch);
        }
        return $str;
    }

    public static function getSerial($fileData, &$errMsg) {

        // 		$fileData = str_replace('\n','',$fileData);
        // 		$fileData = str_replace('\r','',$fileData);

        $start = "-----BEGIN CERTIFICATE-----";
        $end = "-----END CERTIFICATE-----";
        $data = trim ( $fileData );
        if (substr ( $data, 0, strlen ( $start ) ) != $start ||
            substr ( $data, strlen ( $data ) - strlen ( $end ) ) != $end) {
            // echo $fileData;
            $errMsg = "error pem data";
            return false;
        }

        $data = substr ( $data, strlen ( $start ), strlen ( $data ) - strlen ( $end ) - strlen ( $start ) );
        $bindata = base64_decode ( $data );
        $bindata = unpack ( 'C*', $bindata );

        $byte = array_shift ( $bindata );
        if ($byte != 0x30) {
            $errMsg = "1st tag " . $byte . " is not 30";
            return false;
        }

        $length = CertSerialUtil::readLength ( $bindata );
        $byte = array_shift ( $bindata );
        if ($byte != 0x30) {
            $errMsg = "2nd tag " . $byte . " is not 30";
            return false;
        }

        $length = CertSerialUtil::readLength ( $bindata );
        $byte = array_shift ( $bindata );
        // 		echo $byte . "<br>\n";
        if ($byte == 0xa0) { //version tag.
            $length = CertSerialUtil::readLength ( $bindata );
            CertSerialUtil::readData ( $bindata, $length );
            $byte = array_shift ( $bindata );
        }

        // 		echo $byte . "<br>\n";
        if ($byte != 0x02) { //x509v1 has no version tag, x509v3 has.
            $errMsg = "4th/3rd tag " . $byte . " is not 02";
            return false;
        }
        $length = CertSerialUtil::readLength ( $bindata );
        $serial = CertSerialUtil::readData ( $bindata, $length );
        // 		echo bin2hex(CertSerialUtil::toStr( $serial ));
        return CertSerialUtil::bytesToBigInteger($serial);
    }

    private static function readLength(&$bindata) {
        $byte = array_shift ( $bindata );
        if ($byte < 0x80) {
            $length = $byte;
        } else {
            $lenOfLength = $byte - 0x80;
            for($i = 0; $i < $lenOfLength; $i ++) {
                $lenBytes [] = array_shift ( $bindata );
            }
            $length = CertSerialUtil::bytesToInteger ( $lenBytes );
        }
        return $length;
    }

    private static function readData(&$bindata, $length) {
        $data = array ();
        for($i = 0; $i < $length; $i ++) {
            $data [] = array_shift ( $bindata );
        }
        return $data;
    }
}



